
package com.urbansquall.ginger
{
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.utils.Dictionary;

	public class AnimationController extends Bitmap
	{
		
		public override function set rotation( value:Number ):void 
 		{
 			m_rotation = int( value );
 			//Make sure value is between 0-360
 			m_rotation %= 360;
 			if( m_rotation < 0)
 			{
 				m_rotation += 360;
 			}
 			//---------------------------------
 			
 			m_rotationFrame = Math.round( m_rotation / ( m_currentAnimation._degToRotBy ) );
 			//If we are at the 360 frame, set back down to 0 to
 			//eliminate errors
 			if( m_rotationFrame >= m_currentAnimation._framesOfRotation )
 			{
 				m_rotationFrame = 0;
 			}
 			
 			m_hasRotationChanged = true;
 		}
		public override function get rotation():Number { return m_rotation; }
		public function set fps( value:int ) : void 
		{ 
			m_fps = value; 
			
 			if( value <= 0 )
 			{
 				m_fps = 0;
 				m_animationDelay = 0;
 			} 
 			else
 			{
 				m_fps = value;
 				m_animationDelay = 1000 / value;
 			}	
		}
		public function get fps() : int { return m_fps; }		
		public function get defaultAnimationID() : String { return m_defaultAnimationID; }
		public function set defaultAnimationID( value:String ) : void { m_defaultAnimationID = value; }
		public function get currentAnimationID() : String { return m_currentAnimationID; }
		public override function set x(value:Number) : void{ super.x = value - m_center; m_actualX = value; }
		public override function get x() : Number { return super.x - m_center; }
		public override function set y(value:Number) : void{ super.y = value - m_center; m_actualY = value; }
		public override function get y() : Number { return super.y - m_center; }
		public override function get width():Number { throw new Error( "<AnimationController> This function is unavailable!" ); }
		public override function set width(value:Number):void { throw new Error( "<AnimationController> This function is unavailable!" ); }
		public override function get height():Number { throw new Error( "<AnimationController> This function is unavailable!" ); }
		public override function set height(value:Number):void { throw new Error( "<AnimationController> This function is unavailable!" ); }
		
		public function AnimationController(pixelSnapping:String="auto", 
			smoothing:Boolean=false)
		{
			super(null, pixelSnapping, smoothing);
			
			m_IDArray = [];
			m_animationQueue = [];
			m_frames = [];
			m_animationMap = new Dictionary();
			m_framesMap = new Dictionary();
			x = y = 0; //Center the bitmap at creation.
			//fps = DEFAULT_FPS; //Commented this out because if left alone it will default to the FPS of your actual SWF...which is cool.
		}
		
		public function addAnimation( a_animationID : String, a_animation : BitmapAnimation ) : void
		{
			m_IDArray.push( a_animationID );
			m_animationMap[ a_animationID ] = a_animation;
			if( m_defaultAnimationID == null )
			{
				m_defaultAnimationID = a_animationID;
				startNewAnimation( m_defaultAnimationID );
				play( a_animationID );
			}
		}
		
		/**
		 * switches playhead to this animation immediately
		 * @param a_animationID
		 * 
		 */		
		public function play( a_animationID : String ) : void
		{
			if( m_animationMap[ a_animationID ] == null )
			{
				throw new Error( "<AnimationController> Invalid Animation ID!" );
			}
			
			m_isPlaying = true;
			m_currentAnimationID = a_animationID;
			startNewAnimation( a_animationID );
			m_animationQueue = [];
			m_animationQueue.push( m_currentAnimation );
			
		}
		
		public function gotoFrame( a_frameID : String, a_animationID : String = null ) : void
		{
			if( a_animationID != null )
			{
				startNewAnimation( a_animationID );
			}
			
			var index:int = m_framesMap[ a_frameID ];
			m_currentFrame = Frame( m_frames[ index ] );
			bitmapData = m_currentFrame.bitmapData[ m_rotationFrame ] as BitmapData;
			m_frameIndex = index;
			m_framesPassed = 0;
			m_isPlaying = false;
			if( m_currentFrame.frameID != null ){ dispatchEvent( new FrameEvent( FrameEvent.CHANGE, true, m_currentFrame.frameID, m_currentAnimationID ) ); }
			
			
		}
		
		public function isPaused() : Boolean { return m_isPlaying; }		
		public function unpause() : void { if( m_currentAnimation != null ){ m_isPlaying = true; } }		  
		public function pause() : void { m_isPlaying = false; }
		public function togglePause() : void { if( m_currentAnimation != null ){ m_isPlaying = !m_isPlaying; } }
	
		public function update( a_ms : Number ) : void
		{
 			if( m_hasAnimationEnded )
			{
				dispatchEvent( new AnimationEvent( AnimationEvent.CHANGE, false, m_currentAnimationID ) );
				m_animationQueue.shift();
				if( !m_isLooping && m_animationQueue.length == 0 && m_animationQueue[0] == undefined && m_currentAnimationID == m_defaultAnimationID )
				{
					m_isPlaying = false;
				}
				else
				{
					startNewAnimation( m_animationQueue[ 0 ] );
				}
				
				m_hasAnimationEnded = false;
					
			}
			else if( m_hasRotationChanged )
			{
				bitmapData = m_currentFrame.bitmapData[ m_rotationFrame ];
				m_hasRotationChanged = false;
			}
			
			if(m_isPlaying)
 			{
	 			m_timeSinceLastUpdate += a_ms;
	 			if( m_timeSinceLastUpdate > m_animationDelay )
	 			{
	 			 	m_timeSinceLastUpdate -= m_animationDelay;

	 				updateFrame();
	 			} 
 			}
		}
		
		/**
		 * adds the id to an animation queue
         * when the active animation completes
         * the next animation in the queue is begun
		 * @param a_animationID
		 * 
		 */		
		public function queue( a_animationID : String ) : void
		{
			var animation:BitmapAnimation = m_animationMap[ a_animationID ];
			if( animation == null )
			{
				throw new Error( "<AnimationController> Invalid Animation ID!" );
			}
			m_animationQueue.push( a_animationID );
		}
		
		public function copy() : AnimationController
		{
			var AC:AnimationController = new AnimationController();
			for( var i:int = 0; i < m_IDArray.length; i++ )
			{
				var id:String = m_IDArray[ i ];
				AC.addAnimation( id, m_animationMap[ id ] );
			}
			
			AC.defaultAnimationID = m_defaultAnimationID;
			AC.fps = m_fps;
			
			return AC;
		}
		
		
		// -- PRIVATE --
		
		private function startNewAnimation( a_animationID:String ) : void
		{
			m_timeSinceLastUpdate = 0;
			m_currentAnimationID = a_animationID;
			m_currentAnimation = m_animationMap[ m_currentAnimationID ];
			if( m_currentAnimation == null )
			{
				m_currentAnimation = m_animationMap[ m_defaultAnimationID ];
				m_currentAnimationID = m_defaultAnimationID;
			}
			m_frames = m_currentAnimation._frames;
			m_framesMap = m_currentAnimation._frameMap;
			m_isLooping = m_currentAnimation.isLooping;
			
			m_totalFrames = m_frames.length;
			m_frameIndex = 0;
			m_currentFrame = Frame( m_frames[0] );
			m_center = m_currentFrame.center;
			x = m_actualX;
			y = m_actualY;
			bitmapData = m_currentFrame.bitmapData[ m_rotationFrame ];
			dispatchEvent( new AnimationEvent( AnimationEvent.CHANGE, true, m_currentAnimationID ) );
		}
		
		private function updateFrame() : void
		{
			if( m_currentFrame == null ) { return; }
			m_framesPassed++;
			if( m_framesPassed > m_currentFrame.numFrames )
			{
				m_framesPassed = 0;
				if( ++m_frameIndex >= m_totalFrames )
				{
					if( !m_isLooping )
					{
						m_hasAnimationEnded = true;
						return;
					}
					else
					{
						m_frameIndex = 0;
					}
				}
				m_currentFrame = Frame( m_frames[ m_frameIndex ] );
				bitmapData = m_currentFrame.bitmapData[ m_rotationFrame ] as BitmapData;
				if( m_currentFrame.frameID != null ){ dispatchEvent( new FrameEvent( FrameEvent.CHANGE, true, m_currentFrame.frameID, m_currentAnimationID ) ); }
				
			}
		}
		
		private var m_framesPassed:int;
		private var m_totalFrames:int;
		private var m_currentFrame:Frame;
		private var m_frameIndex:int;
		private var m_frames:Array;
		private var m_framesMap:Dictionary;
		private var m_isLooping:Boolean;
        
        private var m_rotationFrame:int;
        private var m_hasRotationChanged:Boolean;
        private var m_rotation:Number;
        private var m_IDArray:Array; //used for copy function
        private var m_hasAnimationEnded:Boolean;
        private var m_animationDelay:int;
        private var m_timeSinceLastUpdate:int;
        private var m_currentAnimation:BitmapAnimation;
        private var m_isPlaying:Boolean;
        private var m_animationMap:Dictionary;
        private var m_animationQueue:Array;
        private var m_currentAnimationID:String;
        private var m_defaultAnimationID:String;
        private var m_fps:int;
        private var m_center:Number;
        private var m_actualX:Number;
        private var m_actualY:Number;
	}
}
	